redis.replicate_commands()

local key = KEYS[1] -- 令牌桶标识
local quantity = 1 -- 需要的令牌数量，默认为1
local timestamp = tonumber(redis.call('time')[1]) -- 当前时间戳
local result = true
local expire = -1 -- 过期时间按照多规则中最大的时间来处理

-- 第一次请求时创建令牌桶
if (redis.call('EXISTS', key) == 0) then
    for i = 1, #ARGV, 2 do
        local quota = tonumber(ARGV[i])
        local period = tonumber(ARGV[i + 1])
        expire = period

        local remainKey = 'r' .. i
        local timeKey = 't' .. i
        redis.call('HMSET', key, remainKey, quota - quantity, timeKey, timestamp + period)
    end
else
    for i = 1, #ARGV, 2 do
        local quota = tonumber(ARGV[i])  -- 时间窗口内的限额
        local period = tonumber(ARGV[i + 1]) -- 时间窗口大小（秒）
        local remainKey = 'r' .. i
        local timeKey = 't' .. i
        expire = period

        -- 计算从上次生成到现在这段时间应该生成的令牌数
        local remain = tonumber(redis.call('HGET', key, remainKey))
        local last_reset = tonumber(redis.call('HGET', key, timeKey))
        local delta_quota = math.floor(((timestamp - last_reset) / period) * quota)
        if (delta_quota > 0) then
            remain = remain + delta_quota
            if (remain > quota) then
                remain = quota
            end
            redis.call('HMSET', key, remainKey, remain, timeKey, timestamp)
        end
        -- 如果剩余令牌数量大于请求数量，则该规则下获取令牌成功
        if (remain < quantity) then
            result = false
        end
    end

    -- 如果所有规则都成功了
    if (result == true) then
        -- 则更新剩余token
        for i = 1, #ARGV, 2 do
            local remainKey = 'r' .. i
            redis.call('HINCRBY', key, remainKey, -quantity)
        end
    end
end
redis.call('EXPIRE', key, expire)
return tostring(result)